package util

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"log"
	"time"
)

type Payload struct {
	UserName string `json:"username"`
}

type CustomClaims struct {
	*jwt.StandardClaims
	*Payload
}

const (
	TokenTtl        = time.Minute * 60 * 24
	RefreshTokenTtl = time.Minute * 60 * 24 * 2
)

var verifyKey = []byte("entry-task")

func GeneratorJwt(userName string) (string, error) {
	var claims = &CustomClaims{
		&jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenTtl).Unix(),
			IssuedAt:  time.Now().Unix(),
		},
		&Payload{
			UserName: userName,
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(verifyKey)
}

func GeneratorRefreshJwt(userName string) (string, error) {
	var claims = &CustomClaims{
		&jwt.StandardClaims{
			ExpiresAt: time.Now().Add(RefreshTokenTtl).Unix(),
			IssuedAt:  time.Now().Unix(),
		},
		&Payload{
			UserName: userName,
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(verifyKey)
}

func ParseJwt(tokenStr string) *jwt.MapClaims {
	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return verifyKey, nil
	})
	if token == nil {
		return nil
	}
	if token.Valid {
		finToken := token.Claims.(jwt.MapClaims)
		return &finToken
	} else if ve, ok := err.(*jwt.ValidationError); ok {
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			log.Printf("%v", ve)
			return nil
		} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
			log.Printf("%v", ve)
			return nil
		} else {
			return nil
		}
	} else {
		return nil
	}
}

func ValidateJwt(tokenStr, username string) bool {
	token, _ := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return verifyKey, nil
	})
	if token != nil && token.Valid {
		claims := token.Claims.(jwt.MapClaims)
		if claims["username"] == username {
			return true
		}
	}
	return false
}

// ValidateTokenClaims go自带json有性能问题，需要换一种方式
func ValidateTokenClaims(token *jwt.Token, username string) bool {
	data, err := json.Marshal(token.Claims)
	if err != nil {
		return false
	}
	var claims jwt.MapClaims
	err = json.Unmarshal(data, &claims)
	id, ok := claims["username"]
	log.Printf("解析得到的token: %v", claims)
	return ok && id == username
}
